package com.bsj.media.network;

import com.bsj.consumerQ.until.ExceptionUntil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http2.Http2FrameCodecBuilder;
import io.netty.handler.codec.http2.Http2MultiplexHandler;
import io.netty.handler.codec.http2.Http2SecurityUtil;
import io.netty.handler.codec.http2.Http2Settings;
import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.net.ssl.SSLException;
import java.io.File;
import java.net.URISyntaxException;
import java.util.Locale;
import java.util.concurrent.TimeUnit;

/**
 * @author yinhao
 * @version 1.0
 * @description 支持HTTP/2.0 多路复用,否则浏览器只支持最多6路直播流同时打开
 * @date 2024/7/22
 */
@Slf4j
@Component
public class Http2NettyUntil {

    @Value("${ssl.privateKey}")
    private String privateKey;

    @Value("${ssl.cert}")
    private String cert;

    private final int CONST_THREAD_BOSS = 4;
    private final int CONST_THREAD_WORKER = 8;
    private ServerBootstrap bootstrap = null;
    private EventLoopGroup boss_group = null;
    private EventLoopGroup worker_group = null;
    //集成证书
    private SslContext sslCtx = null;

    public boolean start(int port) throws SSLException {
        //ssl证书
        sslCtx = SslContextBuilder.forServer(getFile(cert), getFile(privateKey))
                .applicationProtocolConfig(new ApplicationProtocolConfig(
                ApplicationProtocolConfig.Protocol.ALPN,
                ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
                ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
                ApplicationProtocolNames.HTTP_2,
                ApplicationProtocolNames.HTTP_1_1)).build();
        this.bootstrap = new ServerBootstrap();
        String os = System.getProperty("os.name");
        if (os.toLowerCase(Locale.ENGLISH).contains("linux")) {
            this.boss_group = new EpollEventLoopGroup(CONST_THREAD_BOSS);
            this.worker_group = new EpollEventLoopGroup(CONST_THREAD_WORKER);
            bootstrap.channel(EpollServerSocketChannel.class);
        } else {
            this.boss_group = new NioEventLoopGroup(CONST_THREAD_BOSS);
            this.worker_group = new NioEventLoopGroup(CONST_THREAD_WORKER);
            bootstrap.channel(NioServerSocketChannel.class);
        }
        this.bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
        this.bootstrap.group(boss_group, worker_group).childHandler(new ChannelInitializer<SocketChannel>(){
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ChannelPipeline p = ch.pipeline();
                if (sslCtx != null) {
                    p.addLast(sslCtx.newHandler(ch.alloc()));
                }
                // 自动空闲检测，如果10分钟没有接收和发出数据则断开连接
                p.addLast(new IdleStateHandler(0, 0, 10,
                        TimeUnit.MINUTES));
                // 添加http加解码器
                p.addLast(Http2FrameCodecBuilder.forServer()
                        .autoAckSettingsFrame(true)
                        .initialSettings(Http2Settings.defaultSettings()).build(),
                        new HttpRequestHandler());
//                p.addLast(new Http2MultiplexHandler(new HttpRequestHandler()));
            }
        });
        this.bootstrap.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK,
                new WriteBufferWaterMark(1024 * 100, 1024 * 500));
        this.bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
        this.bootstrap.childOption(ChannelOption.TCP_NODELAY, false);
        this.bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
        this.bootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
        try {
            this.bootstrap.bind(port).sync();
        } catch (InterruptedException e) {
            log.error("NettyHttp初始化失败，端口：" + port + " 错误信息：" + ExceptionUntil.getErrorStr(e));
            return false;
        }
        return true;
    }

    private static File getFile(String fileName){
        return new File(fileName);
    }

    public void stop() {
        this.boss_group.shutdownGracefully();
        this.worker_group.shutdownGracefully();
        this.boss_group = null;
        this.worker_group = null;
        this.bootstrap = null;
    }
}
